///闭包是一种匿名类型，一旦声明，就会产生一个新的类型，但这个类型无法被其它地方使用。这个类型就像一个结构体，会包含所有捕获的变量。
///可以将rust闭包简单理解为编译后会存在于可执行文件代码段中的一个结构。
/// 结构中包含一个可以调用的函数，以及可能被需要的，从声明闭包的上下文中捕获的额外数据。
///如果在闭包前面加move关键字，则捕获的变量所有权将被转移到闭包内

//闭包的实现
//Rust 的闭包实现与其它语言有些许不同。它们实际上是trait的语法糖
//理解闭包底层是如何工作的关键有点奇怪：使用()调用函数，像foo()，是一个可重载的运算符。到此，其它的一切都会明了。
// 在Rust中，我们使用trait系统来重载运算符。调用函数也不例外。我们有三个trait来分别重载：
///# mod foo {
/// pub trait Fn<Args> : FnMut<Args> {
///     extern "rust-call" fn call(&self, args: Args) -> Self::Output;
/// }
///
/// pub trait FnMut<Args> : FnOnce<Args> {
///     extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
/// }
///
/// pub trait FnOnce<Args> {
///     type Output;
///
///     extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
/// }
/// # }
/// 你会注意到这些 trait 之间的些许区别，不过一个大的区别是self：Fn获取&self，FnMut获取&mut self，
/// 而FnOnce获取self。这包含了所有3种通过通常函数调用语法的self。不过我们将它们分在 3 个 trait 里，
/// 而不是单独的 1 个。这给了我们大量的对于我们可以使用哪种闭包的控制。
/// 闭包的|| {}语法是上面 3 个 trait 的语法糖。Rust 将会为了环境创建一个结构体，impl合适的 trait，并使用它


//闭包作为参数和返回值
//闭包作为参数（Taking closures as arguments）
//Fn是一个trait，我们可以用它限制我们的泛型
fn call_with_one<F>(some_closure: F) -> i32
    where F : Fn(i32) -> i32 {

    some_closure(1)
}



#[test]
pub fn demo1(){
    let answer = call_with_one(|x| x + 2);

    assert_eq!(3, answer);
}


//函数指针和闭包
//一个函数指针有点像一个没有环境的闭包。因此，你可以传递一个函数指针给任何函数除了作为闭包参数，下面的代码可以工作：
fn call_with_one2(some_closure: &dyn Fn(i32) -> i32) -> i32 {
    some_closure(1)
}
fn add_one(i: i32) -> i32 {
    i + 1
}
#[test]
pub fn demo2(){
    let f = add_one;
    let answer = call_with_one2(&f);
    assert_eq!(2, answer);
}


//返回闭包（Returning closures）
//对于函数式风格代码来说在各种情况返回闭包是非常常见的。如果你尝试返回一个闭包，你可能会得到一个错误。
// 在刚接触的时候，这看起来有点奇怪，不过我们会搞清楚。当你尝试从函数返回一个闭包的时候，你可能会写出类似这样的代码：
//fn factory() -> (Fn(i32) -> i32) {
//     let num = 5;
//
//     |x| x + num
// }
//
// let f = factory();
//
// let answer = f(1);
// assert_eq!(6, answer);
//编译的时候会给出这一长串相关错误

//为了从函数返回一些东西，Rust 需要知道返回类型的大小。不过Fn是一个 trait，它可以是各种大小(size)的任何东西。
// 比如说，返回值可以是实现了Fn的任意类型。一个简单的解决方法是：返回一个引用。因为引用的大小(size)是固定的，
// 因此返回值的大小就固定了。因此我们可以这样写：

fn factory() -> Box<dyn Fn(i32) -> i32> {
    let num = 5;

    Box::new(move |x| x + num)
}

#[test]
pub fn demo3(){
    let f = factory();

    let answer = f(1);

    assert_eq!(6, answer);
}

// FnOnce闭包
//标准库中FnOnce trait的定义为：
// pub trait FnOnce<Args> {
//     type Output;//返回值类型
//     extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
// }
// call_once函数第一个参数为self，也就是闭包自身，该函数一旦被调用，闭包自身的所有权被move到函数内部。因此，外部可以调用FnOnce类型闭包至多一次
#[test]
pub fn demo4(){
    // FnOnce
    let name = String::from("Rust");
    let closure = move |greeting: String|{
        println!("{}", name); 
        //获取了name的所有权
        (greeting, name)
    };
    let(g, n) = closure(String::from("hello"));
    println!("{},{}", g, n);

    //closure失效
    //let(greeting, name) = closure(String::from("world"));

    //name失效
    //println!("{}", name);
}
#[test]
pub fn demo5(){
    // FnOnce
    let name = String::from("Rust");
    let  closure =  |greeting: String|{
        //获取了name所有权
        let m = name;
        println!("{}", m); 
        greeting
    };
    let g = closure("hello".to_string());
    println!("{}", g);

    //closure失效
    //closure("".to_string());

    //name失效
    //println!("{}", name);
 
}


// FnMut闭包
//FnMut闭中可以修改捕获的变量
#[test]
pub fn demo6(){
    let mut name = String::new();
    //不会获取name所有权
    let mut c =  |s: &str|{
        name.push_str(s);
    
    };
    c("helloo");
  
    //可以再次调用
    c(" world");

    //name不会失效
    println!("{}", name);

}
#[test]
pub fn demo7(){
    let mut name = String::new();
    //通过move获取name所有权
    let mut c =  move |s: &str|{
        name.push_str(s);
        println!("{}", &name);
    
    };
    c("helloo");
  
    //可以再次调用
    c(" world");

    //name失效
    //println!("{}", name);

}